package fr.ants.model.ai.behaviour;

import fr.ants.model.Ant;
import fr.ants.model.Grid;
import fr.ants.model.Nest;
import fr.ants.model.Node;
import fr.ants.utils.MathUtils;

/**
 * <p>Another type of ant.</p>
 *
 * @author Vianney DUPOY DE GUITARD
 */
public class QueenBehavior extends AntBehavior {

	/** Steps on explore mode. */
	private static final int EXPLORE_STEPS = 3;

	/** Steps until the queen stops and starts a nest. */
	private Integer maximumSteps = 0;

	/** The last direction the worker took. */
	private Direction previousDirection;

	/** Steps the queen has walked. */
	private int stepCounter = 0;

	/** 
	 * Creates a new queen behavior.
	 * @param lifespan : lifespan of the ant.
	 */
	public QueenBehavior(int lifespan) {
		setAntState(AntState.EXPLORING);
		maximumSteps = lifespan - EXPLORE_STEPS * 3;
	}

	/** {@inheritDoc} */
	public void live(Ant ant, Grid grid) {
		// Tant que la reine n'a pas terminé son exploration ou qu'elle se trouve sur un noeud avec une fourmillière,
		// elle continue son exploration.
		if (stepCounter < maximumSteps || ant.getNode().getNest() != null) {
			switch (getAntState()) {
			case EXPLORING:
				explore(ant, grid);
				break;
			} 
		} else {
			Nest newNest = new Nest(ant.getNode(), 5, ant.getFood());
			newNest.copy(ant.getNest());
			grid.addNest(newNest);
			ant.kill();
		}
	}

	/** {@inheritDoc} */
	public void reset() { }

	/**
	 * Actions triggered when the ant is exploring.
	 * @param ant : the ant exploring.
	 */
	private void explore(Ant ant, Grid grid) {
		Node node = ant.getNode();

		// Creates a path if the ant can't walk anymore.
		while (ant.getPath().isEmpty() || !ant.getPath().peek().isWalkable()) {
			Direction d = findDirection();
			previousDirection = d;
			ant.getPath().clear();
			switch (d) {
			case NORTH:
				// Let's go north.
				for (int i = EXPLORE_STEPS; i > 0; i--) {
					ant.getPath().add(grid.getNode(node.getX(), node.getY() - i));
				}
				break;
			case SOUTH:
				// Let's go south.
				for (int i = EXPLORE_STEPS; i > 0; i--) {
					ant.getPath().add(grid.getNode(node.getX(), node.getY() + i));
				}
				break;
			case EAST:
				// Let's go east.
				for (int i = EXPLORE_STEPS; i > 0; i--) {
					ant.getPath().add(grid.getNode(node.getX() + i, node.getY()));
				}
				break;
			case WEST:
				// Let's go west.
				for (int i = EXPLORE_STEPS; i > 0; i--) {
					ant.getPath().add(grid.getNode(node.getX() - i, node.getY()));
				}
				break;
			}
		}

		// Move the ant.
		Node dest = ant.getPath().pop();
		grid.moveAnt(node, dest, ant);
		stepCounter++;
	}

	/** @return a new direction. */
	private Direction findDirection() {
		Direction[] directions;
		int index;
		if (previousDirection == null) {
			// First direction the ant take, totally random.
			directions = Direction.values();
			index = MathUtils.rnd(0, 3);
		} else {
			// Find a random direction on a different axis than the former direction.
			index = MathUtils.rnd(0, 1);
			switch (previousDirection) {
			case NORTH:
			case SOUTH:
				directions = new Direction[] {Direction.WEST, Direction.EAST};
				break;
			case WEST:
			case EAST:
				directions = new Direction[] {Direction.NORTH, Direction.SOUTH};
				break;
			default:
				directions = Direction.values();
				index = MathUtils.rnd(0, 3);
				break;
			}
		}

		return directions[index];
	}

	/**
	 * A direction an ant can take.
	 * @author Vianney DUPOY DE GUITARD
	 */
	private enum Direction {
		NORTH,
		SOUTH,
		WEST,
		EAST;
	}

}